<template>
    <el-form
        ref="formRef"
        size="default"
        :model="formData"
        label-width="90px"
        v-bind="$attrs"
    >
        <el-row v-bind="row">
            <template v-for="(item, index) in formList" :key="item.prop ? item.prop + index : index">
                <el-col v-bind="item.col" v-if="isShowFormItem(item)">
                    <template v-if="item.type === `custom`">
                        <template v-if="item.render">
                            <BaseRender :render="item.render" v-bind="{ item, formData, formList }"></BaseRender>
                        </template>
                        <template v-else-if="item.prop && slots[item.prop]">
                            <slot :name="item.prop" v-bind="{ item, formData, formList }"></slot>
                        </template>
                        <template v-else-if="item.component">
                            <component :is="item.component" v-model="formData[item.prop]" v-bind="item.componentProps"></component>
                        </template>
                    </template>
                    <template v-else-if="item.type === `title`">
                        <div class="title-wrapper">
                            <div class="block"></div>
                            <div class="title">
                                <template v-if="item.render">
                                    <BaseRender :render="item.render" v-bind="{ item, formData, formList }"></BaseRender>
                                </template>
                                <template v-else-if="item.prop && slots[item.prop]">
                                    <slot :name="item.prop" v-bind="{ item, formData, formList }"></slot>
                                </template>
                                <template v-else-if="item.component">
                                    <component :is="item.component" v-model="formData[item.prop]" v-bind="item.componentProps"></component>
                                </template>
                                <template v-else-if="item.label">
                                    {{ item.label }}
                                </template>
                            </div>
                        </div>
                    </template>
                    <el-form-item v-else v-bind="filterItem(item)">
                        <template v-if="item.render">
                            <BaseRender :render="item.render" v-bind="{ item, formData, formList }"></BaseRender>
                        </template>
                        <template v-else-if="item.prop && slots[item.prop]">
                            <slot :name="item.prop" v-bind="{ item, formData, formList }"></slot>
                        </template>
                        <template v-else-if="item.component">
                            <component :is="item.component" v-model="formData[item.prop]" v-bind="item.componentProps"></component>
                        </template>
                        <template v-else-if="item.type === `input`">
                            <el-input
                                v-model="formData[item.prop]"
                                v-bind="filterBindMap.defaultFilterBind(item.props)"
                            >
                                <template v-for="(value, key) in item.slots || {}" :key="key" #[key]>
                                    <BaseRender :render="value" v-bind="{ item, formData, formList }"></BaseRender>
                                </template>
                            </el-input>
                        </template>
                        <template v-else-if="item.type === `number`">
                            <el-input
                                v-model="formData[item.prop]"
                                :oninput="
                                    (e) => {
                                        e.target.value
                                            .replace(/[^\d.]/g, '')
                                            .replace(/^\./g, '')
                                            .replace(/\.{2,}/g, '.')
                                            .replace('.', '$#$')
                                            .replace(/\./g, '')
                                            .replace('$#$', '.')
                                    }
                                "
                                @input="val => {
                                    if (val.endsWith('.')) {
                                        formData[item.prop] = val
                                    } else {
                                        formData[item.prop] = isNaN(parseFloat(val)) ? '' : parseFloat(val)
                                    }
                                }"
                                v-bind="filterBindMap.defaultFilterBind(item.props)"
                            >
                                <template v-for="(value, key) in item.slots || {}" :key="key" #[key]>
                                    <BaseRender :render="value" v-bind="{ item, formData, formList }"></BaseRender>
                                </template>
                            </el-input>
                        </template>
                        <template v-else-if="item.type === `input-number`">
                            <el-input-number
                                v-model="formData[item.prop]"
                                v-bind="filterBindMap.defaultFilterBind(item.props)"
                            >
                                <template v-for="(value, key) in item.slots || {}" :key="key" #[key]>
                                    <BaseRender :render="value" v-bind="{ item, formData, formList }"></BaseRender>
                                </template>
                            </el-input-number>
                        </template>
                        <template v-else-if="item.type === `select`">
                            <BaseSelect
                                v-model="formData[item.prop]"
                                class="w-100%"
                                v-bind="filterBindMap.defaultFilterBind(item.props)"
                            >
                                <template v-for="(value, key) in item.slots || {}" :key="key" #[key]>
                                    <BaseRender :render="value" v-bind="{ item, formData, formList }"></BaseRender>
                                </template>
                            </BaseSelect>
                        </template>
                        <template v-else-if="item.type === `tree-select`">
                            <el-tree-select v-model="formData[item.prop]" v-bind="filterBindMap.defaultFilterBind(item.props)">
                                <template v-for="(value, key) in item.slots || {}" :key="key" #[key]>
                                    <BaseRender :render="value" v-bind="{ item, formData, formList }"></BaseRender>
                                </template>
                            </el-tree-select>
                        </template>
                        <template v-else-if="item.type === `radio-group`">
                            <BaseRadioGroup
                                v-model="formData[item.prop]"
                                v-bind="filterBindMap.defaultFilterBind(item.props)"
                            >
                                <template v-for="(value, key) in item.slots || {}" :key="key" #[key]>
                                    <BaseRender :render="value" v-bind="{ item, formData, formList }"></BaseRender>
                                </template>
                            </BaseRadioGroup>
                        </template>
                        <template v-else-if="item.type === `checkbox-group`">
                            <BaseCheckboxGroup
                                v-model="formData[item.prop]"
                                v-bind="filterBindMap.defaultFilterBind(item.props)"
                            >
                                <template v-for="(value, key) in item.slots || {}" :key="key" #[key]>
                                    <BaseRender :render="value" v-bind="{ item, formData, formList }"></BaseRender>
                                </template>
                            </BaseCheckboxGroup>
                        </template>
                        <template v-else-if="item.type === `switch`">
                            <el-switch
                                v-model="formData[item.prop]"
                                v-bind="filterBindMap.defaultFilterBind(item.props)"
                            >
                                <template v-for="(value, key) in item.slots || {}" :key="key" #[key]>
                                    <BaseRender :render="value" v-bind="{ item, formData, formList }"></BaseRender>
                                </template>
                            </el-switch>
                        </template>
                        <template v-else-if="item.type === `date-picker`">
                            <el-date-picker
                                v-model="formData[item.prop]"
                                style="width: 100%;"
                                v-bind="filterBindMap.defaultFilterBind(item.props)"
                            >
                                <template v-for="(value, key) in item.slots || {}" :key="key" #[key]>
                                    <BaseRender :render="value" v-bind="{ item, formData, formList }"></BaseRender>
                                </template>
                            </el-date-picker>
                        </template>
                        <template v-else-if="item.type === `time-picker`">
                            <el-time-picker
                                v-model="formData[item.prop]"
                                style="width: 100%;"
                                v-bind="filterBindMap.defaultFilterBind(item.props)"
                            >
                                <template v-for="(value, key) in item.slots || {}" :key="key" #[key]>
                                    <BaseRender :render="value" v-bind="{ item, formData, formList }"></BaseRender>
                                </template>
                            </el-time-picker>
                        </template>
                        <template v-else-if="item.type === `cascader`">
                            <el-cascader
                                v-model="formData[item.prop]"
                                v-bind="filterBindMap.defaultFilterBind(item.props)"
                            >
                                <template v-for="(value, key) in item.slots || {}" :key="key" #[key]>
                                    <BaseRender :render="value" v-bind="{ item, formData, formList }"></BaseRender>
                                </template>
                            </el-cascader>
                        </template>
                    </el-form-item>
                </el-col>
            </template>
        </el-row>
    </el-form>
</template>

<script setup>
import { ref, toValue, unref, useAttrs, useSlots } from 'vue'
import BaseRender from './BaseRender.vue'
import BaseSelect from './BaseSelect.vue'
import BaseCheckboxGroup from './BaseCheckboxGroup.vue'
import BaseRadioGroup from './BaseRadioGroup.vue'

defineOptions({
    name: 'BaseForm',
    inheritAttrs: false
})

const formRef = ref(null)
const props = defineProps({
    row: {
        type: Object,
        default: () => ({})
    },
    formList: {
        type: Array,
        default: () => []
    },
    formData: {
        type: Object,
        default: () => ({})
    },
    defaultFormData: {
        type: [Function, Object],
        default: () => ({})
    }
})

Object.assign(props.formData, toValue(props.defaultFormData))

const attrs = useAttrs()
const slots = useSlots()

const filterItem = item => {
    const { col, type, render, ...defaultArgs } = item
    switch (type) {
        default:
            return defaultArgs
    }
}

const isShowFormItem = item => {
    const { isShow = true } = item
    if (typeof isShow === 'function') return isShow(props.formData)
    return isShow
}

const filterBindMap = {
    defaultFilterBind: (item = {}) => {
        const { ...args } = item
        return args
    }
}

const validate = async (fullError = false) => {
    try {
        await unref(formRef).validate()
        return true
    } catch (err) {
        if (fullError) return err
        return false
    }
}

defineExpose({
    formRef,
    validate
})
</script>

<style lang="scss" scoped>
.el-form-item {
    :deep() {
        .el-input-number {
            &.text-align-left {
                .el-input__inner {
                    text-align: left;
                }
            }
        }
    }
}

.title-wrapper {
    flex: 1 0;
    min-width: 0;
    display: flex;
    align-items: center;
    padding-bottom: 18px;

    .block {
        width: 4px;
        height: 17px;
        background-color: var(--el-color-primary);
        margin-right: 8px;
    }

    .title {
        font-size: 18px;
        font-weight: 600;
        color: var(--el-text-color-regular);
    }
}

</style>